use std::fmt;

use from_pest::FromPest;
use pest::iterators;
use pest_ast::FromPest;
use tracing::debug;
use tracing::trace;

use crate::ast_define::MetaInfo;
use crate::ast_define::Rule;
use crate::ast_define::algebraic_expr::*;
use crate::ast_define::bool_expr::*;
use crate::ast_define::declear::Decl;
use crate::ast_define::function::FuncDef;
use crate::ast_define::get_meta_info;
use crate::ast_define::keyword::INT;
use crate::ast_define::literal::*;

// * 编译单元 CompUnit -> Decl | FuncDef
#[allow(clippy::large_enum_variant)]
#[derive(Debug, PartialEq)]
pub enum CompUnit {
    Decl(Decl),
    FuncDef(FuncDef),
}

impl fmt::Display for CompUnit {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            CompUnit::Decl(this) => write!(f, "{this}"),
            CompUnit::FuncDef(this) => write!(f, "{this}"),
        }
    }
}

impl<'pest> FromPest<'pest> for CompUnit {
    type Rule = Rule;
    type FatalError = from_pest::Void;
    #[tracing::instrument(skip_all)]
    fn from_pest(
        pest: &mut iterators::Pairs<'pest, Rule>,
    ) -> Result<Self, from_pest::ConversionError<from_pest::Void>> {
        trace!("正在解析 CompUnit");
        let pair = pest.next().ok_or(from_pest::ConversionError::NoMatch)?;
        trace!("pair in CompUnit:\n{pair:#?}");
        let Rule::CompUnit = pair.as_rule() else {
            trace!("\n{:#?} != Rule::CompUnit", pair.as_rule());
            return Err(from_pest::ConversionError::NoMatch);
        };
        let mut inner = pair.into_inner();
        trace!("inner:\n{inner:#?}");
        let inner_pair = inner.peek().ok_or(from_pest::ConversionError::NoMatch)?;
        trace!("inner_pair:\n{inner_pair:#?}");
        match inner_pair.as_rule() {
            Rule::Decl => Ok(CompUnit::Decl(Decl::from_pest(&mut inner)?)),
            Rule::FuncDef => Ok(CompUnit::FuncDef(FuncDef::from_pest(&mut inner)?)),
            _ => Err(from_pest::ConversionError::NoMatch),
        }
    }
}

// * 表达式 // 注：SysY 表达式是 int 型表达式
#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::Exp))]
pub struct Exp {
    pub add_exp: AddExp,
    #[pest_ast(outer(with(get_meta_info)))]
    pub meta_info: MetaInfo,
}

impl fmt::Display for Exp {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.add_exp)
    }
}

// * 条件表达式
#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::Cond))]
pub struct Cond {
    pub l_or_exp: LOrExp,
    #[pest_ast(outer(with(get_meta_info)))]
    pub meta_info: MetaInfo,
}

impl fmt::Display for Cond {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.l_or_exp)
    }
}

// * 左值表达式 LVal = { Ident ~ ("[" ~ Exp ~ "]")* }
#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::LVal))]
pub struct LVal {
    pub ident: Ident,
    pub exps: Vec<Exp>,
    #[pest_ast(outer(with(get_meta_info)))]
    pub meta_info: MetaInfo,
}

impl fmt::Display for LVal {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.ident)?;
        for exp in self.exps.iter() {
            write!(f, "[{exp}]")?;
        }
        Ok(())
    }
}

// * 基本表达式 PrimaryExp -> '(' Exp ')' | Number | LVal
#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::Exp_Parenthesis_Wrapper))]
pub struct Exp_Parenthesis_Wrapper {
    pub exp: Exp,
    #[pest_ast(outer(with(get_meta_info)))]
    pub meta_info: MetaInfo,
}

impl fmt::Display for Exp_Parenthesis_Wrapper {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "({})", self.exp)
    }
}

#[derive(Debug, PartialEq)]
pub enum PrimaryExp {
    Exp_Parenthesis_Wrapper(Exp_Parenthesis_Wrapper),
    Number(Number),
    LVal(LVal),
}

impl fmt::Display for PrimaryExp {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            PrimaryExp::Exp_Parenthesis_Wrapper(this) => write!(f, "{this}"),
            PrimaryExp::Number(this) => write!(f, "{this}"),
            PrimaryExp::LVal(this) => write!(f, "{this}"),
        }
    }
}

impl<'pest> FromPest<'pest> for PrimaryExp {
    type Rule = Rule;
    type FatalError = from_pest::Void;
    #[tracing::instrument(skip_all)]
    fn from_pest(
        pest: &mut iterators::Pairs<'pest, Rule>,
    ) -> Result<Self, from_pest::ConversionError<from_pest::Void>> {
        trace!("正在解析 PrimaryExp");
        let pair = pest.next().ok_or(from_pest::ConversionError::NoMatch)?;
        trace!("pair in PrimaryExp:\n{pair:#?}");
        let Rule::PrimaryExp = pair.as_rule() else {
            trace!("\n{:#?} != Rule::PrimaryExp", pair.as_rule());
            return Err(from_pest::ConversionError::NoMatch);
        };

        let mut inner = pair.into_inner();
        trace!("inner:\n{inner:#?}");
        let inner_pair = inner.peek().ok_or(from_pest::ConversionError::NoMatch)?;
        trace!("inner_pair:\n{inner_pair:#?}");
        match inner_pair.as_rule() {
            Rule::Exp_Parenthesis_Wrapper => Ok(PrimaryExp::Exp_Parenthesis_Wrapper(
                Exp_Parenthesis_Wrapper::from_pest(&mut inner)?,
            )),
            Rule::Number => Ok(PrimaryExp::Number(Number::from_pest(&mut inner)?)),
            Rule::LVal => Ok(PrimaryExp::LVal(LVal::from_pest(&mut inner)?)),
            _ => Err(from_pest::ConversionError::NoMatch),
        }
    }
}

// * 基本类型
#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::BType))]
pub struct BType {
    pub int: INT,
    #[pest_ast(outer(with(get_meta_info)))]
    pub meta_info: MetaInfo,
}

impl fmt::Display for BType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.int)
    }
}
